﻿using System;
using System.Collections.Generic;
using System.Data.Linq;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using CrawlerCore;
using HtmlAgilityPack;
using WCrawlerLib;
using System.Security.Cryptography;

using WCrawlerLib.WebSession;
using WCrawlerLib.WebSession.IPBoard;
using WCrawlerLib.WebSession.phpBB;
using WCrawlerLib.WebSession.vBulletin;

namespace WCrawlerLib.WebSession
{
    public abstract class ForumSessionBase : WebSessionBase, IForumSession
    {
        #region Properties

        private Forum _objForum;
        public Forum ObjForum
        {
            get { return _objForum; }
            set { _objForum = value; }
        }

        public string PostParams { get; set; }

        public Dictionary<string, string> ParamDict { get; set; }

        #endregion

        public IForumSession GetInstance(int forumId)
        {
            IForumSession objForumSession = new EmptyForumSession();
            Forum objForum = new Forum();
            objForum = objForum.GetForum(forumId);

            if (objForum.ForumTypeId.HasValue)
            {
                switch ((enumForumType)objForum.ForumTypeId.Value)
                {
                    case enumForumType.vBulletin:
                        objForumSession = new WCrawlerLib.WebSession.vBulletin.VBulletinForumSession();
                        break;
                    case enumForumType.vBulletin3x:
                        objForumSession = new WCrawlerLib.WebSession.vBulletin.VBulletinForumSession();
                        break;
                    case enumForumType.vBulletin4x:
                        objForumSession = new WCrawlerLib.WebSession.vBulletin.VBulletinForumSession();
                        break;
                    case enumForumType.phpBB:
                        objForumSession = new WCrawlerLib.WebSession.phpBB.PhpBBForumSession();
                        break;
                    case enumForumType.phpBB2:
                        objForumSession = new WCrawlerLib.WebSession.phpBB.PhpBBForumSession();
                        break;
                    case enumForumType.phpBB3:
                        objForumSession = new WCrawlerLib.WebSession.phpBB.PhpBBForumSession();
                        break;
                    case enumForumType.IPB:
                        objForumSession = new WCrawlerLib.WebSession.IPBoard.IpBoardForumSession();
                        break;
                    case enumForumType.IPB31x:
                        objForumSession = new WCrawlerLib.WebSession.IPBoard.IpBoardForumSession();
                        break;
                    case enumForumType.IPB32x:
                        objForumSession = new WCrawlerLib.WebSession.IPBoard.IpBoardForumSession();
                        break;
                    case enumForumType.myBB:
                        //_browser = new BrowserSession_myBB();
                        break;
                }
            }
            objForumSession.ObjForum = objForum;
            return objForumSession;
        }

        public bool DoLogin()
        {
            //Init login params
            PostParams = InitLoginParams(_objForum.LoginParams, _objForum.Account.UserName, _objForum.Account.Password);
            string loginUrl = GeneralClass.GetFullUrl(_objForum.ForumUrl, _objForum.LoginUrlPattern);
            Post(loginUrl);
            //get login status
            _objForum.LoginStatus = CheckLoginStatus();
            //Save login status in database
            _objForum.SubmitChanges();
            return _objForum.LoginStatus;
        }

        public void PostNewThread(Post objPost)
        {
            if (ObjForum.DoLogin())
            {
                //======================================================================================
                //Get exactly PostDetail with forumtype and language of the forum
                PostDetail objPostDetail = new PostDetail();
                objPostDetail = objPostDetail.GetExactlyPostDetail(objPost.PostId, ObjForum.LanguageId);
                if (objPostDetail == null)
                    return;

                string title = objPostDetail.PostTitle;
                string message = ConvertHtmlContent(objPostDetail);

                //======================================================================================
                //Check the forum use tag list
                string tagList = "";
                if (ObjForum.IsTags && !string.IsNullOrEmpty(objPost.TagList))
                    tagList = objPost.TagList;

                //======================================================================================
                //Check the forum use icon
                string iconId = "";
                if (ObjForum.IsIcons)
                {
                    int icon = objPost.GetHostIconByPost(objPost.PostId);
                    if (icon > 0)
                        iconId = icon.ToString();
                }

                ////======================================================================================
                ////Check use Hide tag
                ////bool useHideTag = false;
                //if (objForum.IsHideTag)
                //    isHideTag = true;

                ////======================================================================================
                ////Check the forum contraint Hosts
                //bool isHostContraint = false;
                //if (objForum.IsHostContraint)
                //        isHostContraint = true;

                //======================================================================================
                //Get f value of thread id of a forum
                ForumTopic objForumTopic = new ForumTopic();
                objForumTopic = objForumTopic.GetExactlyPostTopicValue(ObjForum.ForumId, objPost.TopicId);
                string fId = objForumTopic.fValue.Value.ToString();
                string prefix = objForumTopic.Prefix;

                PostNewThread(title, message, fId, iconId, tagList, prefix);

                //======================================================================================
                //Get thread num and post num in response url after post successfully
                if (HtmlWeb.StatusCode == HttpStatusCode.OK)
                {
                    //SearchThreadPostNum(objPostDetail);
                }

            }
        }

        public abstract bool CheckLoginStatus();
        
        public abstract string InitLoginParams(string postParams, params string[] pars);

        public abstract string InitEditPostParams(string postParams, params string[] pars);

        public abstract string InitNewReplyParams(string postParams, params string[] pars);

        public abstract string InitNewThreadParams(string postParams, params string[] pars);

        public abstract void InitGeneralPostParams(string postParams, params string[] pars);

        public abstract void PostNewThread(string subject, string message, string fId, string iconId, string tagList,
                                           string prefixId);

        public abstract bool PostNewReply(string topicUrl, string newReplyUrl, string postParams, string message);

        public abstract bool PostEdit(string forumUrl, string topicUrl, string postId, string editParams, string subject,
                                      string message, string updateReason, string icon);


        public abstract int GetAccountId();

        /// <summary>
        /// Convert template html content into full html content
        /// </summary>
        /// <param name="objPostDetail"></param>
        /// <returns></returns>
        public virtual string ConvertHtmlContent(PostDetail objPostDetail)
        {
            //==================================================================================================================
            //Get poster and screenshot Images
            ImagePost objImgPost = new ImagePost();
            Dictionary<int, string> imgDict = objImgPost.GetImagesByPostWithTag(objPostDetail.PostId.Value);
            //Get Poster Image
            string fullHtmlContent = objPostDetail.PostContent.Replace("[POSTER]", imgDict[0]);
            fullHtmlContent = fullHtmlContent.Replace("[SCREENSHOTS]", imgDict[1]);

            //==================================================================================================================
            //Get downloadfile
            FilePost objFilePost = new FilePost();
            string downloadFiles = objFilePost.GetHtmlCode(objPostDetail.PostId.Value, ObjForum.IsHideTag, ObjForum.IsHostContraint);
            if (fullHtmlContent.Contains("[DOWNLOADS]"))
                fullHtmlContent = fullHtmlContent.Replace("[DOWNLOADS]", downloadFiles);
            else
                fullHtmlContent += string.Format("\n[B][U]Download links:[/U][/B]\n{0}", downloadFiles);
            return fullHtmlContent;
        }

        /// <summary>
        /// Use alexa rank to indentify the rank and location of the forum
        /// </summary>
        /// <param name="webUrl"></param>
        /// <returns></returns>
        public Dictionary<string, string> GetAlexaRankAndLocation(string webUrl)
        {
            Dictionary<string, string> result = new Dictionary<string, string>();
            result.Add("rank", "");
            result.Add("location", "");

            string url = string.Format("http://www.alexa.com/search?q={0}&r=home_home&p=bigtop", webUrl);
            Get(url);

            //detect alexa rank
            //string xpath = ".//ul[@class='traffic-stats']/li[1]/a/text()";
            //string xpath = ".//div[@id='search']/div/div[2]/span[1]/a/text()";
            string xpath = ".//div[@id='search']//div[@class='row']/span[1]/a[contains(@href,'#trafficstats')]/text()";
            HtmlNodeCollection ncRank = HtmlDoc.DocumentNode.SelectNodes(xpath);
            if (ncRank != null)
            {
                foreach (HtmlNode node in ncRank)
                {
                    if (node.InnerHtml.Replace("\n", "") != "No Data")
                        result["rank"] = int.Parse(node.InnerHtml.Replace("\n", ""), System.Globalization.NumberStyles.Number).ToString();
                }

                //detect location of forum
                //xpath = ".//ul[@class='traffic-stats']/li[2]/a";
                //xpath = ".//div[@id='search']/div/div[2]/span[2]/a";
                xpath = ".//div[@id='search']//div[@class='row']/span[2]/a";
                HtmlNodeCollection ncLocation = HtmlDoc.DocumentNode.SelectNodes(xpath);
                if (ncLocation != null)
                {
                    foreach (HtmlNode node in ncLocation)
                        result["location"] = node.Attributes["title"].Value;
                }
            }
            //else
            //{
            //    int i = 0;
            //    i += 1;
            //}
            return result;
        }

        
        /// <summary>
        /// Get content of forum:
        /// 1. Indentify type of a forum: vBulletin, phpbb2, phpbb3, IPB
        /// 2. Indentify language of a forum
        /// 3. Indentify content of a forum: App, App-Porn, Porn, ...
        /// 4. Indentify exactly forum type of a forum include version: vBulletin x.x.x, phpbb2, phpbb3, IP.Board x.x.x
        /// 5. Indentify statistics numbers of a forum: Total threads, posts, members, current members, current guests
        /// </summary>
        public void CheckForumInfo()
        {
            try
            {
                //*********************************************************************************
                //GET ALEXA RANK & LOCATION
                //*********************************************************************************
                Dictionary<string, string> alexa = GetAlexaRankAndLocation(ObjForum.ForumUrl);
                if (alexa["rank"] != "")
                    ObjForum.AlexaRank = int.Parse(alexa["rank"]);
                if (alexa["location"] != "")
                {
                    Location loc = new Location();
                    ObjForum.LocationId = loc.GetLocationId(alexa["location"]);
                }

                //Check forum is enable
                if (!ObjForum.IsApproved)
                {
                    //Start load home page
                    string defaultUrl = ObjForum.DefaultUrl == null ? string.Empty : ObjForum.DefaultUrl;
                    HtmlAgilityPack.HtmlDocument doc = Get(ObjForum.ForumUrl + "/" + defaultUrl);
                    StatusCode status = new StatusCode();
                    ObjForum.StatusCodeId = status.GetStatusCodeId((int)HtmlWeb.StatusCode);
                    if (HtmlWeb.StatusCode == HttpStatusCode.OK)
                    {
                        string responeUrl = GeneralClass.FormatUrl(Response.ResponseUri.AbsoluteUri);
                        //Update ForumUrl again if change
                        if (ObjForum.ForumUrl != responeUrl)
                            ObjForum.ForumUrl = responeUrl;

                        //*********************************************************************************
                        //GET FORUM TYPE
                        //*********************************************************************************
                        if (ObjForum.ForumTypeId == (int)enumForumType.Unknown)
                        {
                            ForumType objForumType = new ForumType();
                            Dictionary<string, string> type = GetForumType(doc);
                            objForumType = objForumType.GetForumTypeId(type["type"]);
                            ObjForum.ForumTypeId = objForumType.ForumTypeId;
                            ObjForum.Version = type["version"];
                        }

                        //If the forum is exists
                        if (ObjForum.ForumTypeId.Value != (int)enumForumType.Unknown) //Unknown
                        {
                            //*********************************************************************************
                            //LOGIN
                            //*********************************************************************************
                            if (ObjForum.DoLogin())
                            {
                                //GET USER ID OF ACCOUNT
                                ObjForum.UserId = ObjForum.GetUserId();
                            }

                            //*********************************************************************************
                            //GET LANGUAGE
                            //*********************************************************************************
                            GoogleTranslate objTranslate = new WebSession.GoogleTranslate();
                            if (ObjForum.LanguageId == (int)enumLanguage.Unknown)
                            {
                                //Get texts in forum
                                string htmlText = doc.GetContentText();
                                //User Google Translation to get Language and translated content

                                string[] translateInfo = objTranslate.TranslateContent(htmlText, 500, "auto", "en");
                                Language lang = new Language();
                                ObjForum.LanguageId = lang.GetLangId(translateInfo[0]);
                            }

                            //*********************************************************************************
                            //GET CONTENT: App, App-Porn, Porn
                            //*********************************************************************************
                            if (ObjForum.ContentTypeId == (int)enumContentType.Unknown)
                            {
                                int contentTypeId = (int)enumContentType.Unknown;
                                //Get blacklist pattern of links in forum 
                                ForumTemplateDetail objForumTemplateDetail = new ForumTemplateDetail();

                                //string xPathLink = objForumTemplateDetail.GetXPathLink(objForum.ForumId, objForum.ForumTypeId);
                                string blackListUrl = objForumTemplateDetail.GetBlackList_Pattern(ObjForum.ForumId, ObjForum.ForumTypeId);
                                Dictionary<string, string> forumLinks = doc.GetAllLinks(ObjForum.ForumUrl, blackListUrl);
                                if (forumLinks.Count() > 0)
                                {
                                    string transContent = string.Join(" ", forumLinks.Values.ToArray()).ToLower();
                                    transContent = System.Web.HttpUtility.HtmlDecode(transContent).Replace("&", "");
                                    //Nếu không phải là ngôn ngữ English thì translate lại
                                    if (ObjForum.LanguageId != (int)enumLanguage.English)
                                        transContent = objTranslate.TranslateContent(transContent, 500, "auto", "en")[1];
                                    ContentType cType = new ContentType();
                                    if (ObjForum.AccountId.HasValue) //1. Nếu có account ở forum
                                    {
                                        contentTypeId = cType.IdentifyForumContent(transContent);
                                    }
                                    else //2. Nếu chưa có account login
                                    {
                                        //Vào trang login của forum để xem có tồn tại trang login này không
                                        if (ObjForum.ForumTypeId.HasValue)
                                        {
                                            string loginUrl = objForumTemplateDetail.GetLoginUrl(ObjForum.ForumId, ObjForum.ForumTypeId);
                                            HtmlAgilityPack.HtmlDocument loginDoc = Get(ObjForum.ForumUrl + "/" + loginUrl);
                                            //2.1 Sau khi vào trang login, kiểm tra xem trang login có chứa 'username' và 'password' không?
                                            if (HtmlWeb.StatusCode == HttpStatusCode.OK)
                                                contentTypeId = cType.IdentifyForumContent(transContent);
                                        }
                                    }
                                }
                                ObjForum.ContentTypeId = contentTypeId;
                            }

                            //*********************************************************************************
                            //Get statistics numbers: total threads, post, members, current members, current guest
                            //*********************************************************************************
                            //GetStatisticsNumbers(doc);
                        }
                    }
                }
            }
            catch (WebException we)
            {
                ObjForum.ErrorReport = we.Message;
            }
            catch (Exception ex)
            {
                ObjForum.ErrorReport = ex.Message;
            }

            try
            {
                ObjForum.SubmitChanges();
                
            }
            catch (ChangeConflictException e)
            {
                //throw new ChangeConflictException(e.Message);
                throw e;
            }
        }

        /// <summary>
        /// Return result in two key "type" and "forumtype" of Dictionary
        /// </summary>
        /// <param name="doc"></param>
        /// <returns></returns>
        public Dictionary<string, string> GetForumType(HtmlDocument doc)
        {
            Dictionary<string, string> list = new Dictionary<string, string>();
            list.Add("type", "*Unknown*");
            list.Add("version", "");
            //string htmlContent = System.Web.HttpUtility.HtmlEncode(doc.DocumentNode.InnerHtml.ToLower());
            string htmlContent = doc.DocumentNode.InnerHtml.ToLower();
            //*****************************************************************
            //Search phpBB3
            //*****************************************************************
            string pattern = @"ucp\.php\?mode\=(login|register|logout)";
            Regex r = new Regex(pattern);
            Match m = r.Match(htmlContent);
            if (m.Success)
            {
                list["type"] = "phpbb3";
                list["version"] = "3";
            }

            //*****************************************************************
            //Search phpBB2
            //*****************************************************************
            if (list["type"] == "*Unknown*")
            {
                pattern = @"profile\.php\?mode\=(register|editprofile)";
                r = new Regex(pattern);
                m = r.Match(htmlContent);
                if (m.Success)
                {
                    list["type"] = "phpbb2";
                    list["version"] = "2";
                }
            }

            //*****************************************************************
            //Search ipBoard
            //*****************************************************************
            if (list["type"] == "*Unknown*")
            {
                //pattern = @"app\=core\&amp\;(amp\;|)module\=global\&amp\;(amp\;|)section\=(login|register|login\&amp\;(amp\;|)do\=logout)";
                pattern = @"app\=core\&module\=global\&section\=(login|register)";
                r = new Regex(pattern);
                m = r.Match(htmlContent);
                if (m.Success)
                {
                    //Get Version
                    pattern = @"ipbv\=(?<version>([0-9]+))";
                    //pattern = @"(?<type>ip(\.|)board)(</a>|)\s*(?<version>([0-9]\.[0-9]\.[0-9]|))";
                    r = new Regex(pattern);
                    m = r.Match(htmlContent);
                    if (m.Success)
                    {
                        if (!string.IsNullOrEmpty(m.Result("${version}").ToString()))
                        {
                            list["version"] = m.Result("${version}").ToString();
                            if (list["version"].Contains("31"))
                                list["type"] = "IPB31x";
                            else if (list["version"].Contains("32"))
                                list["type"] = "IPB32x";
                        }
                    }
                    else
                    {
                        list["type"] = "IPB";
                    }
                }
            }

            //******************************************************************************
            //Search vBulletin 
            //******************************************************************************
            if (list["type"] == "*Unknown*")
            {
                //First, find patterns as "vbulletin x.x.x"
                //pattern = @"vbulletin(\&\#8482\;|\&reg\;|\&trade\;|)(</a>|)\s*(version|)\s*((?<version>([0-9]\.[0-9]\.[0-9]))|)";
                pattern = @"vbulletin";
                if (htmlContent.Contains(pattern))
                {
                    if (Response.Cookies["bblastactivity"] != null)
                        list["type"] = "vBulletin3x";
                    else if (Response.Cookies["bb_lastactivity"] != null)
                        list["type"] = "vBulletin4x";
                    else
                        list["type"] = "vBulletin";

                    pattern = @"vbulletin.*(version|)\s*(?<version>([0-9]+\.[0-9]+\.[0-9]+))";
                    r = new Regex(pattern);
                    m = r.Match(htmlContent);
                    if (m.Success)
                    {
                        //Search forum type again
                        if (!string.IsNullOrEmpty(m.Result("${version}").ToString()))
                        {
                            list["version"] = m.Result("${version}").ToString();
                            if (list["type"] == "*Unknown*" || list["type"] == "vBulletin")
                            {
                                if (list["version"][0].ToString() == "3")
                                    list["type"] = "vBulletin3x";
                                else if (list["version"][0].ToString() == "4")
                                    list["type"] = "vBulletin4x";
                            }
                        }
                    }
                }
            }
            return list;
        }
    }
}
